package test;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileEncryption {
	static String input = "D:\\Research\\20200522_ImageEncrpytion\\WindowsFormsApp1\\bin\\Debug\\GameOfThroneS01E01.mkv"; // 可以是任何大文件
	static String output = "D:\\Research\\20200522_ImageEncrpytion\\WindowsFormsApp1\\bin\\Debug\\GameOfThroneS01E01_out.mkv";
	static String output1 = "D:\\Research\\20200522_ImageEncrpytion\\WindowsFormsApp1\\bin\\Debug\\GameOfThroneS01E01_out1.mkv";
	static String key = "SunBeach.jpg";

	public static void main(String[] args) throws Exception {
		// 使用密钥对指定的文件加密。
		Encrypt(input, output, key, 0);
		Encrypt(output, output1, key, 1);
	}

	public static String Info = null;
	public static int maxsize = 1048576;
	static int[] ones = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4,
			5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2,
			3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4,
			5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
			4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
			6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4,
			5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6,
			7, 5, 6, 6, 7, 6, 7, 7, 8 };


	public static byte[] readKey(String file, int length){
		byte[] key;
		
		
		return key;
	}

	public static boolean Encrypt(String infile, String outfile, String keyfile, int type) throws Exception {
		byte[] key = readBytes(keyfile, maxsize);
		FileInputStream fin = new FileInputStream(infile);
		FileOutputStream fout = new FileOutputStream(outfile);
		long total = fin.available();
		byte[] bts = new byte[maxsize];
		double t1 = 0, t2 = 0, t3 = 0;
		int readCount = maxsize;
		byte bx = 0b1010110;
		while (readCount >= maxsize) {
			long dt1 = System.nanoTime();
			readCount = fin.read(bts, 0, maxsize);
			long dt2 = System.nanoTime();
			bx = getBX(bts, key, type);
			// System.out.println("Bx=" + (bx & 0xff));
			for (int i = 0; i < readCount; i++)
				bts[i] ^= key[i] ^ bx;
			long dt3 = System.nanoTime();
			fout.write(bts, 0, readCount);
			long dt4 = System.nanoTime();

			t1 += dt2 - dt1;
			t2 += dt3 - dt2;
			t3 += dt4 - dt3;
		}
		fin.close();
		fout.close();

		t1 /= 1000000000.0;
		t2 /= 1000000000.0;
		t3 /= 1000000000.0;

		String speed_reading = String.format("%.6f", total / t1 / 1073741824);
		String speed_encryption = String.format("%.6f", total / t2 / 1073741824);
		String speed_writing = String.format("%.6f", total / t3 / 1073741824);

		Info = String.format("总用时：%.4f s, 总容量：%.4f GB.\n", t1 + t2 + t3, total / 1024.0 / 1024 / 1024);
		Info += String.format("读取用时  %4f s, 速度：%s GB/s\n", t1, speed_reading);
		Info += String.format("加密用时  %4f s, 速度：%s GB/s\n", t2, speed_encryption);
		Info += String.format("写入用时  %4f s, 速度：%s GB/s\n", t3, speed_writing);
		System.out.println(Info);
		return true;
	}

	private static byte getBX(byte[] bts, byte[] key, int etype) {
		return etype == 0 ? encrypt(bts, key) : decrypt(bts, key);
	}

	private static byte decrypt(byte[] bts, byte[] key) {
		if (bts.length <= 1024)
			return 0;

		int offset = 0, pos = 0;
		for (int i = 0, w = 1; i < 8; i++, w *= 2)
			offset += ((ones[bts[i] & 0xff] + 100 - ones[key[i] & 0xff]) % 2) * w;
		// System.out.println("offset2: " + (offset & 0xff));

		for (int i = 0, w = 1; i < 8; i++, w *= 2) {
			int even = (ones[bts[(i + offset) % bts.length] & 0xff] + 100 - ones[key[i + offset] & 0xff]) % 2;
			pos += even == 0 ? 0 : w;
		}

		byte b = (byte) (pos & 0xff);
		while (ones[b & 0xff] % 2 > 0)
			b = (byte) (b << 1);

		return b;
	}

	private static byte encrypt(byte[] bts, byte[] key) {
		if (bts.length <= 1024)
			return (byte) 0xff;
		int offset = 0, pos = 0;
		for (int i = 0, w = 1; i < 8; i++, w *= 2)
			offset += ones[bts[i] & 0xff] % 2 == 0 ? 0 : w;

		for (int i = 0, w = 1; i < 8; i++, w *= 2) {
			int odd = (ones[bts[(i + offset) % bts.length] & 0xff]) % 2;
			pos += odd == 0 ? 0 : w;
		}

		byte b = (byte) (pos & 0xff);
		while (ones[b & 0xff] % 2 > 0)
			b = (byte) (b << 1);

		return b;
	}

	public static byte[] readBytes(String keyfile, int maxsize) throws Exception {
		byte[] bts = new byte[maxsize];
		FileInputStream fs = new FileInputStream(keyfile);
		int left = maxsize, p = 0;
		while (left > 0) {
			p += fs.read(bts, maxsize - left, left);
			left -= p;
		}
		fs.close();
		return bts;
	}
}
